package com.ruoyi.framework.security.service

import com.ruoyi.common.constant.*
import com.ruoyi.common.exception.user.UserPasswordNotMatchException
import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException
import com.ruoyi.common.utils.MessageUtils
import com.ruoyi.common.utils.SecurityUtils
import com.ruoyi.framework.manager.AsyncManager
import com.ruoyi.framework.manager.factory.AsyncFactory
import com.ruoyi.framework.redis.RedisCache
import com.ruoyi.framework.security.context.AuthenticationContextHolder
import com.ruoyi.project.system.domain.SysUser
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import java.util.concurrent.*

/**
 * 登录密码方法
 *
 * @author ruoyi
 */
@Component
class SysPasswordService {
    @Autowired
    private val redisCache: RedisCache? = null

    @Value(value = "\${user.password.maxRetryCount}")
    private val maxRetryCount = 0

    @Value(value = "\${user.password.lockTime}")
    private val lockTime = 0

    /**
     * 登录账户密码错误次数缓存键名
     *
     * @param username 用户名
     * @return 缓存键key
     */
    private fun getCacheKey(username: String): String {
        return CacheConstants.PWD_ERR_CNT_KEY + username
    }

    fun validate(user: SysUser) {
        val usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext()
        val username = usernamePasswordAuthenticationToken.name
        val password = usernamePasswordAuthenticationToken.credentials.toString()
        var retryCount = redisCache!!.getCacheObject(getCacheKey(username)).toString().toIntOrNull()
        if (retryCount == null) {
            retryCount = 0
        }
        when {
            Integer.valueOf(maxRetryCount).toInt() <= retryCount -> {
                AsyncManager.me().execute(
                    AsyncFactory.recordLogininfor(
                        username, Constants.LOGIN_FAIL,
                        MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime)
                    )
                )
                throw UserPasswordRetryLimitExceedException(maxRetryCount, lockTime)
            }
            !matches(user, password) -> {
                retryCount += 1
                AsyncManager.me().execute(
                    AsyncFactory.recordLogininfor(
                        username, Constants.LOGIN_FAIL,
                        MessageUtils.message("user.password.retry.limit.count", retryCount)
                    )
                )
                redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES)
                throw UserPasswordNotMatchException()
            }
            else -> {
                clearLoginRecordCache(username)
            }
        }
    }

    fun matches(user: SysUser, rawPassword: String?): Boolean {
        return SecurityUtils.matchesPassword(rawPassword, user.password)
    }

    fun clearLoginRecordCache(loginName: String) {
        if (redisCache!!.hasKey(getCacheKey(loginName))) {
            redisCache.deleteObject(getCacheKey(loginName))
        }
    }
}
